#ifndef TIEC_FOUNDATION_H
#define TIEC_FOUNDATION_H

#include "macro.h"
#include "data.h"
#include "file.h"

namespace NS_TIEC {
  static String kSourceExtension = ".t";
  static String kIrExtension = ".tir";
  static String kBytecodeExtension = ".tc";

  /// 输入文件类型定义
  enum struct SourceKind {
    /// 源代码
    kSyntax = 0,
    /// IR中间代码
    kIr = 1,
    /// 字节码
    kBytecode = 2
  };

  /// 编译器输入文件抽象接口
  class Source {
  public:
    /// 获取输入文件的名称
    /// \return 文件名
    virtual String getName() = 0;

    /// 获取文件最后修改时间
    /// \return 时间戳
    virtual UInt64 lastModified() = 0;

    /// 读取文件内容
    /// \return 文件的全部内容
    virtual String readContent() = 0;

    /// 获取URI
    /// \return URI
    virtual Uri getUri() = 0;

    /// 获取文件路径
    /// \return 文件绝对路径
    virtual String getPath() = 0;

    /// 添加子源文件
    virtual void addChildSource(const PTR<Source>& source) = 0;

    /// 获取输入源关联子输入源合并编译
    /// \return 子输入源集合
    virtual const List<PTR<Source>> childSources() = 0;

    /// 获取文件类型
    /// \return 当前文件的类型
    virtual SourceKind getKind() = 0;
  };

  /// 本地文件输入源
  class FileSource : public Source {
  public:
    explicit FileSource(File&& file);
    explicit FileSource(const File& file);

    String getName() override;

    UInt64 lastModified() override;

    String readContent() override;

    Uri getUri() override;

    String getPath() override;

    void addChildSource(const PTR<Source>& source) override;

    const List<PTR<Source>> childSources() override;

    SourceKind getKind() override;
  private:
    File file_;
    List<PTR<Source>> child_sources_;
  };

  /// 文本Source
  class StringSource : public Source {
  public:
    explicit StringSource(const String& name, const String& content, const Uri& uri);

    String getName() override;

    UInt64 lastModified() override;

    String readContent() override;

    Uri getUri() override;

    String getPath() override;

    void addChildSource(const PTR<Source> &source) override;

    const List<PTR<Source>> childSources() override;

    SourceKind getKind() override;
  private:
    String name_;
    String content_;
    Uri uri_;
  };

  /// PTR<TiecodeSource>的hash计算函数
  struct SourcePtrHash {
    const size_t operator()(const PTR<Source>& source) const {
      return source->getUri().hash();
    }
  };
  /// PTR<TiecodeSource>的值比对函数
  struct SourcePtrEqual {
    const size_t operator()(const PTR<Source>& a, const PTR<Source>& b) const {
      return a->getUri() == b->getUri();
    }
  };

  /// 位置描述
  struct Position {
    size_t line;
    size_t column;
    size_t index;

    /// 将当前Position加上指定长度得到新的position
    /// \param length 长度
    /// \return 新的Position
    Position plusColumn(size_t length) const;

  };

  /// 范围描述
  struct Range {
    Position start;
    Position end;

    /// 判断Range是否包含某个Position
    /// @param position 光标位置
    const bool contains(const Position& position) const;

    /// 获取Range的总行数
    /// @return 行数
    const size_t lineCount() const;
  };

  /// 语法树位置范围描述
  struct Origin {
    Int64 source_id {-1};
    Range range;
  };

  /// 名称接口定义
  class Name {
  public:
    /// 转换为String
    virtual String& asString() = 0;

    /// 转换为String
    virtual String asString() const = 0;

    /// 转换为C字符串
    virtual const char* asCString() = 0;

    /// 获取字符长度
    /// \return 总字符长度
    virtual size_t length() = 0;

    /// 判断内容是否和某个字符串相同
    /// \param text 文本
    /// \return 相同返回true，否则false
    virtual bool contentSameAs(const String& text) = 0;

    /// 计算Name的哈希值
    virtual size_t hash() = 0;
  };

  class StringName: public Name {
  public:
    StringName(const String& str);

    String& asString() override;

    String asString() const override;

    const char* asCString() override;

    size_t length() override;

    bool contentSameAs(const String& text) override;

    size_t hash() override;
  private:
    String str_;
  };

  /// PTR<Name>的哈希函数
  struct NamePtrHash {
    bool operator()(const PTR<Name>& name) const {
      return name->hash();
    }
  };

  /// PTR<Name>的值比对函数
  struct NamePtrEqual {
    bool operator()(const PTR<Name>& a, const PTR<Name>& b) const {
      return a == b;
    }
  };

  /// 日志等级
  enum struct LogLevel {
    kLevelDebug = 0,
    kLevelInfo = 1,
    kLevelWarning = 2,
    kLevelError = 3,
    kLevelFatal = 4,
  };

  /// 诊断信息
  struct Diagnostic {
    String uri;
    Range range;
    String key;
    String message;
    LogLevel level{LogLevel::kLevelDebug};

    Diagnostic();

    explicit Diagnostic(const String& message);

    Diagnostic(const String& message, LogLevel level);

    Diagnostic(const String& uri, const Range& range, const String& message, const String& key);

    Diagnostic(const String& uri, const Range& range, const String& key, const String& message, LogLevel level);
  };

  /// 编译器任务类型
  enum struct TaskKind {
    /// 语法树解析阶段
    kParse = 0,
    /// 符号表填充阶段
    kEnter,
    /// 语法树标注阶段
    kAttribute,
    /// 语句低级化阶段
    kLower,
    /// 最终输出阶段
    kFinal
  };

  /// 将 TaskKind 转换为可读String
  /// @param kind TaskKind
  const char* taskKindToString(const TaskKind& kind);

  /// 源文件行号结构(用于业务)
  struct SourceLineView {
    /// 结绳源文件路径
    String path {};
    /// 结绳源文件的行号
    size_t line {0};

    bool valid() const;
    static SourceLineView kEmpty;
  };

  /// 源文件行号结构(用于存储)
  struct SourceLine {
    /// 结绳源文件ID
    Int64 source_id {-1};
    /// 结绳源文件的行号
    size_t line {0};

    bool valid() const;
    static SourceLine kEmpty;
  };

  /// 每个类的行号映射
  struct LineMapping {
    /// 置入一行到结绳源文件中语法树开始位置行号的映射
    /// @param output_line 编译输出文件中的行号
    /// @param origin 结绳源文件中语法树的位置
    void putStartLine(size_t output_line, const Origin& origin);

    /// 置入一行到结绳源文件中语法树结束位置行号的映射
    /// @param output_line 编译输出文件中的行号
    /// @param origin 结绳源文件中语法树的位置
    void putEndLine(size_t output_line, const Origin& origin);

    /// 置入一行到结绳源文件中语法树位置行号的映射
    /// @param output_line 编译输出文件中的行号
    /// @param source_line 结绳源文件中语法树的位置
    void putSourceLine(size_t output_line, const SourceLine& source_line);

    /// 根据输出文件中指定行获取其对应结绳源文件的行号
    /// @param output_line 编译输出文件中的行号
    /// @return SourceLine
    const SourceLine& getSourceLine(size_t output_line) const;

    /// 遍历行号表
    /// @param consumer 遍历函数
    void forEachSourceLine(std::function<void(size_t, const SourceLine&)>&& consumer) const;
  private:
    friend class SourceMappingEncoder;
    /// 输出文件的行号映射
    /// key: 输出文件(如 rn_xx.java)中的行号
    /// value: 结绳源文件(.t)中的行号，同一个输出文件中用到了结绳语法树可能是多个.t源文件组合的，所以每行映射都要存sourceId
    HashMap<size_t, SourceLine> line_map_;
  };

  /// 源文件映射表，用于还原编译后位置信息
  class SourceMapping {
  public:
    /// 从行号表文件反序列化SourceMapping
    /// @param path 行号表文件路径
    static SourceMapping decodeFromFile(const String& path);

    /// 将行号表序列化到本地文件
    /// @param path 行号表文件存储路径
    /// @return 序列化成功返回true
    [[nodiscard]] bool encodeToFile(const String& path) const;

    /// 置入一个输出文件的行号映射表
    /// @param output_file 输出的文件名(如rn_xx.java)
    /// @param mapping 行号映射表
    void putLineMapping(const String& output_file, LineMapping&& mapping);

    /// 置入一个名称映射
    /// @param mangled_name 经过mangle的名称
    /// @param original_name 符号原始名称
    void putNameMapping(const String& mangled_name, const String& original_name);

    /// 根据输出的文件名和行号获取对应的结绳源代码原始文件和行号信息
    /// @param output_file 输出的文件名(如rn_xx.java)
    /// @param output_line 输出文件中的行号(如rn_xx.java中编译报错行)
    /// @return SourceLineView
    SourceLineView getSourceLineView(const String& output_file, size_t output_line);

    /// 获取输出名称对应结绳源代码中的原始名称
    /// @param mangled_name 输出的名称
    /// @return 结绳源代码中的原始名称
    String getOriginalName(const String& mangled_name);

    String& getSourcePathById(Int64 source_id);
    void putSourcePathForId(Int64 source_id, const String& path);
#ifdef TIEC_DEBUG
    void dumpLines();
    void dumpNames();
#endif
  private:
    friend class SourceMappingEncoder;
    /// 每个源文件ID对应的文件路径
    /// key: 源文件ID
    /// value: 源文件路径
    HashMap<Int64, String> source_map_;
    /// 每个文件名对应的行号映射
    /// key: 输出文件名(比如 rn_xx.java)
    /// value: 输出文件中每行行号到结绳源文件的映射
    HashMap<String, LineMapping> lines_map_;
    /// 名称映射表
    /// key: 输出的名称 (如 rn_xx)
    /// value: 结绳源代码中的原始名称 (如 启动类)
    HashMap<String, String> name_map_;
  };
}
#endif //TIEC_FOUNDATION_H
